<?php
/**
 * Copyright 2011  SURFfoundation
 * 
 * This file is part of ESCAPE.
 * 
 * ESCAPE is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * ESCAPE is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with ESCAPE.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * For more information:
 * http://escapesurf.wordpress.com/
 * http://www.surffoundation.nl/
 * 
 * Contact: d.vierkant@utwente.nl
 * 
 * @license http://www.gnu.org/licenses/gpl.html GNU GPLv3
 */
?>
<?php
/**
 * HTML META metadata extractor
 * 
 * Extracts metadata stored in HTML META tags. Both basic fields (title, description, creator) and embedded dublin core (dc.*) are supported.
 * 
 * TODO: more meta tags are listed on http://scholar.google.com/intl/en/scholar/inclusion.html
 */
class metaextractor_extractor_HtmlMeta extends metaextractor_Extractor
{
	protected $name = "HTML META";
	protected $supportedUrlPattern = null;
	
	/**
	 * (non-PHPdoc)
	 * @see escape/metaextractor/extractor/metaextractor_Extractor::processUrl()
	 */
	public function processUrl($url, $urlContentFetcher)
	{
		$results = array();
		$metadataDocuments = array();
		
		$urlContent = $urlContentFetcher->getUrlContent($url);
		
		// try to get a more accurate subject URI from the HTTP headers
		$httpHeaders = $urlContent->getHttpHeaders();
		$subjectUri = metaextractor_Util::extractPrimaryTopicUriFromHttpHeaders($httpHeaders, $url);
		
		// only accept complete (X)HTML pages
		$mimeType = $urlContent->getMimeType();
		if($urlContent->isComplete() && ($mimeType == 'text/html' || $mimeType == 'application/xhtml+xml')) // perhaps also 'application/xml' and 'text/xml'
		{
			// fetch the text content in UTF8
			$html = $urlContent->getUtf8TextContent();
			
			// try to get a more accurate subject URI from the (X)HTML content
			if($subjectUri === null)
			{
				$subjectUri = metaextractor_Util::extractPrimaryTopicUriFromHtml($html, $url);
			}
			
			// use the URL as the subject URI if no subject URI was extracted
			if($subjectUri === null)
			{
				$subjectUri = $url;
			}
						
			// create a result document
			$rdfDocument = simplerdf_RdfDocument::createRdfDocument();
			$rdfResource = $rdfDocument->createResource($subjectUri);
			
			// extract META tags
			preg_match_all('~<meta [^>]+>~ims', $html, $matches);
			//print_r($matches);
			$metaTags = $matches[0];
			
			// parse META tags
			$metaContents = array();
			foreach($metaTags as $metaTag)
			{
				$name = null;
				$content = null;
				
				preg_match_all('~([-a-z]+) *= *((?:"[^"]*")|(?:\'[^\']*\'))~ims', $metaTag, $matches);
				foreach($matches[1] as $attributeIdx => $attributeName)
				{
					$attributeValue = trim(html_entity_decode(substr($matches[2][$attributeIdx], 1, -1)));
					//print $attributeName . "=" . $attributeValue . "\n";
					
					$attributeName = strtolower($attributeName);
					if($attributeName == 'name')
					{
						$name = $attributeValue;
					}
					else if($attributeName == 'content')
					{
						$content = $attributeValue;
					}
				}
				
				if($name !== null && $content !== null && $content !== '')
				{
					//print $name . "=" . $content . "\n";
					$metaContents[strtolower($name)][] = $content;
				}
			}
			//print_r($metaContents);
			
			// title
			$objects = self::getFirstArrayKeyMatch($metaContents, array('citation_title', 'dc.title', 'title'));
			if($objects === null)
			{
				$objects = array();
				
				// extract HTML title
				preg_match_all('~<title[^>]*>([^<]+)</title>~ims', $html, $matches);
				$htmlTitles = $matches[1];
				if(count($htmlTitles) > 0)
				{
					foreach($htmlTitles as $htmlTitle)
					{
						$objects[] = trim(html_entity_decode($htmlTitle));
					}
				}
			}
			$rdfResource->addStatement('http://purl.org/dc/terms/title', $objects);
			
			// creator
			$objects = self::getFirstArrayKeyMatch($metaContents, array('citation_author', 'citation_authors', 'dc.creator', 'creator'));
			if($objects !== null)
			{
				$objects = array_unique($objects);
				foreach($objects as $object)
				{
					// create a foaf:Agent object for every creator 
					$relatedRdfResource = $rdfDocument->createResource();
					$rdfResource->addStatement('http://purl.org/dc/terms/creator', $relatedRdfResource->getSubject());
					$relatedRdfResource->addStatement('http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'http://xmlns.com/foaf/0.1/Agent');
					$relatedRdfResource->addStatement('http://xmlns.com/foaf/0.1/name', $object);
				}
			}
			
			// description
			$objects = self::getFirstArrayKeyMatch($metaContents, array('dc.description', 'description'));
			$rdfResource->addStatement('http://purl.org/dc/terms/description', $objects);
			
			// date
			$objects = self::getFirstArrayKeyMatch($metaContents, array('dc.date', 'date'));
			$rdfResource->addStatement('http://purl.org/dc/terms/date', $this->convertDates($objects));

			// date created
			$objects = self::getFirstArrayKeyMatch($metaContents, array('dc.date.created', 'dc.created'));
			$rdfResource->addStatement('http://purl.org/dc/terms/created', $this->convertDates($objects));
			
			// date valid
			$objects = self::getFirstArrayKeyMatch($metaContents, array('dc.date.valid', 'dc.valid'));
			$rdfResource->addStatement('http://purl.org/dc/terms/valid', $this->convertDates($objects));
			
			// date available
			$objects = self::getFirstArrayKeyMatch($metaContents, array('dc.date.available', 'dc.available'));
			$rdfResource->addStatement('http://purl.org/dc/terms/available', $this->convertDates($objects));
			
			// date issued
			$objects = self::getFirstArrayKeyMatch($metaContents, array('citation_date', 'dc.date.issued', 'dc.issued'));
			$rdfResource->addStatement('http://purl.org/dc/terms/issued', $this->convertDates($objects));
			
			// date modified
			$objects = self::getFirstArrayKeyMatch($metaContents, array('dc.date.modified', 'dc.modified'));
			$rdfResource->addStatement('http://purl.org/dc/terms/modified', $this->convertDates($objects));
			
			// date accepted
			$objects = self::getFirstArrayKeyMatch($metaContents, array('dc.date.dateaccepted', 'dc.date.accepted', 'dc.dateaccepted'));
			$rdfResource->addStatement('http://purl.org/dc/terms/dateAccepted', $this->convertDates($objects));
			
			// date copyrighted
			$objects = self::getFirstArrayKeyMatch($metaContents, array('dc.date.datecopyrighted', 'dc.date.copyrighted', 'dc.datecopyrighted'));
			$rdfResource->addStatement('http://purl.org/dc/terms/dateCopyrighted', $this->convertDates($objects));
			
			// date submitted
			$objects = self::getFirstArrayKeyMatch($metaContents, array('dc.date.datesubmitted', 'dc.date.submitted', 'dc.datesubmitted'));
			$rdfResource->addStatement('http://purl.org/dc/terms/dateSubmitted', $this->convertDates($objects));
			
			// rights
			$objects = self::getFirstArrayKeyMatch($metaContents, array('dc.rights', 'rights'));
			$rdfResource->addStatement('http://purl.org/dc/terms/rights', $objects);
			
			if(count($rdfResource->getPredicates()) > 0)
			{
				$metadataDocuments[] = $rdfDocument;
			}
		}
		
		// create the result objects
		foreach($metadataDocuments as $metadataDocument)
		{
			$result = new metaextractor_Result();
			$result->setExtractorId($this->getId());
			$result->setExtractorName($this->getName());
			$result->setSubjectUri($subjectUri);
			$result->setRdfDocument($metadataDocument);
			
			$results[] = $result;
		}
		
		return $results;
		
	}
	
	/**
	 * Get the first match from an array using a set of keys.
	 * @param array $array array to search
	 * @param array $keys key to use for the search
	 * @return $match value for matching key
	 */
	protected static function getFirstArrayKeyMatch($array, $keys)
	{
		foreach($keys as $key)
		{
			if(isset($array[$key]))
			{
				return $array[$key];
			}
		}
		
		return null;
	}
	
	/**
	 * Convert dates.
	 * @param unknown_type $dates
	 */
	protected static function convertDates($dates)
	{
		$convertedDates = array();
		
		if($dates)
		{
			foreach($dates as $date)
			{
				if(preg_match('~([0-9][0-9]?)/([0-9][0-9]?)/([0-9][0-9][0-9][0-9])~', $date, $matches))
				{
					// 9/22/2000 -> 2000-09-20
					$date = $matches[3] . '-' . str_pad($matches[1], 2, '0', STR_PAD_LEFT) . '-' . str_pad($matches[2], 2, '0', STR_PAD_LEFT);
				}
				else if(preg_match('~([0-9][0-9][0-9][0-9])/([0-9][0-9]?)/([0-9][0-9]?)~', $date, $matches))
				{
					// 2011/6/2 -> 2011-06-2
					$date = $matches[1] . '-' . str_pad($matches[2], 2, '0', STR_PAD_LEFT) . '-' . str_pad($matches[3], 2, '0', STR_PAD_LEFT);
				}
				
				$convertedDates[] = $date;
			}
		}
		
		return $convertedDates;
	}
}